#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <tss2/tss2_common.h>
#include <tss2/tss2_rc.h>
#include <tss2/tss2_sys.h>
#include <tss2/tss2_tctildr.h>
#include <tss2/tss2_tpm2_types.h>

#ifndef TCTI_NAME
#define TCTI_NAME "mssim"
#endif

#define CHECK_RC(rc, message, handle) \
    do { \
        if ((rc) != TSS2_RC_SUCCESS) { \
            fprintf(stderr, "%s:%d 错误: %s 失败! TSS2_RC: 0x%x, %s\n", \
                    __FILE__, __LINE__, (message), (rc), Tss2_RC_Decode(rc)); \
            goto cleanup; \
        } \
    } while (0)

#define CHUNK_SIZE 1024

const char* algid_to_name(TPM2_ALG_ID alg);
TSS2_RC init_tcti_context(TSS2_TCTI_CONTEXT **tcti_context);
TSS2_RC init_sys_context(TSS2_TCTI_CONTEXT *tcti_context, TSS2_SYS_CONTEXT **sys_context);
TSS2_RC startup_tpm(TSS2_SYS_CONTEXT *sys_context);
void clean(TSS2_TCTI_CONTEXT *tcti_context, TSS2_SYS_CONTEXT *sys_context);
TSS2_RC sequence_hash(TSS2_SYS_CONTEXT *sys_context, FILE *fp,
    TPM2B_DIGEST *out_hash, TPMI_ALG_HASH hash_alg);

int main() {
    TSS2_RC rc = TSS2_RC_SUCCESS;
    TSS2_TCTI_CONTEXT *tcti_context = NULL;
    TSS2_SYS_CONTEXT *sys_context = NULL;

    /* --- 初始化 TCTI Context --- */
    rc = init_tcti_context(&tcti_context);
    CHECK_RC(rc, "初始化 TCTI Context", tcti_context);

    /* --- 初始化 SAPI Context --- */
    rc = init_sys_context(tcti_context, &sys_context);
    CHECK_RC(rc, "初始化 SAPI Context", sys_context);

    /* --- 启动 TPM --- */
    rc = startup_tpm(sys_context);
    CHECK_RC(rc, "启动 TPM", sys_context);

    /* --- Sequence Hash --- */
    const char *file_path = "test.bin";
    FILE *fp = fopen(file_path, "rb");
    if (fp == NULL) {
        fprintf(stderr, "无法打开文件: %s\n", file_path);
        goto cleanup;
    }
    TPM2B_DIGEST out_hash;
    TPMI_ALG_HASH hash_alg = TPM2_ALG_SHA256;
    rc = sequence_hash(sys_context, fp, &out_hash, hash_alg);
    CHECK_RC(rc, "计算序列哈希值", sys_context);
    fclose(fp);
    if (out_hash.size > 0) {
        printf("计算得到的序列哈希值为: ");
        for (size_t i = 0; i < out_hash.size; i++) {
            printf("%02X ", out_hash.buffer[i]);
        }
        printf("\n");
    }


    /* --- 清理资源 --- */
cleanup:
    clean(tcti_context, sys_context);

    return !(rc == TSS2_RC_SUCCESS);
}

// 初始化 TCTI Context
TSS2_RC init_tcti_context(TSS2_TCTI_CONTEXT **tcti_context) {
    return Tss2_TctiLdr_Initialize(TCTI_NAME, tcti_context);
}

// 初始化 SAPI Context
TSS2_RC init_sys_context(TSS2_TCTI_CONTEXT *tcti_context, TSS2_SYS_CONTEXT **sys_context) {
    if (tcti_context == NULL) {
        return TSS2_SYS_RC_BAD_REFERENCE;
    }

    size_t sys_context_size = Tss2_Sys_GetContextSize(0);
    *sys_context = (TSS2_SYS_CONTEXT *)malloc(sys_context_size);
    if (*sys_context == NULL) {
        return TSS2_BASE_RC_MEMORY;
    }

    TSS2_ABI_VERSION abi_version = TSS2_ABI_VERSION_CURRENT;
    TSS2_RC rc = Tss2_Sys_Initialize(*sys_context, sys_context_size, tcti_context, &abi_version);
    if (rc != TSS2_RC_SUCCESS) {
        free(*sys_context);
        *sys_context = NULL;
    }

    return rc;
}

// 启动 TPM
TSS2_RC startup_tpm(TSS2_SYS_CONTEXT *sys_context) {
    if (sys_context == NULL) {
        return TSS2_SYS_RC_BAD_REFERENCE;
    }

    TSS2_RC rc = Tss2_Sys_GetCapability(sys_context, NULL,
                                        TPM2_CAP_TPM_PROPERTIES, TPM2_PT_MANUFACTURER,
                                        1, NULL, NULL, NULL);
    if (rc == TPM2_RC_INITIALIZE) {
        rc = Tss2_Sys_Startup(sys_context, TPM2_SU_CLEAR);
    }

    return rc;
}

TSS2_RC sequence_hash(TSS2_SYS_CONTEXT *sys_context, FILE *fp,
    TPM2B_DIGEST *out_hash, TPMI_ALG_HASH hash_alg) {
    if (sys_context == NULL) {
        return TSS2_SYS_RC_BAD_REFERENCE;
    }

    TPMI_DH_OBJECT sequence_handle = TPM2_RH_NULL;
    TPM2B_MAX_BUFFER chunk = { 0 };
    TPM2B_AUTH auth = { 0 };
    TPMT_TK_HASHCHECK validation = { 0 };

    const char* password = "HelloWorld";
    auth.size = strlen(password);
    memcpy(auth.buffer, password, auth.size);

    TSS2_RC rc = Tss2_Sys_HashSequenceStart(
        sys_context,
        NULL,
        &auth,
        hash_alg,
        &sequence_handle,
        NULL
    );
    if (rc != TSS2_RC_SUCCESS) { return rc; }


    TSS2L_SYS_AUTH_COMMAND cmd_auths_array = { 0 };
    cmd_auths_array.count = 1;
    cmd_auths_array.auths[0].sessionHandle = TPM2_RS_PW;
    cmd_auths_array.auths[0].nonce.size = 0;
    cmd_auths_array.auths[0].hmac = auth;
    cmd_auths_array.auths[0].sessionAttributes = 0;

    size_t bytes_read;
    while ((bytes_read = fread(chunk.buffer, 1, CHUNK_SIZE, fp)) > 0) {
        chunk.size = (UINT16)bytes_read;
        rc = Tss2_Sys_SequenceUpdate(
            sys_context,
            sequence_handle,
            &cmd_auths_array,
            &chunk,
            NULL
        );
        if (rc != TSS2_RC_SUCCESS) { return rc; }
    }

    chunk.size = 0;
    rc = Tss2_Sys_SequenceComplete(sys_context,
        sequence_handle,
        &cmd_auths_array,
        &chunk,
        TPM2_RH_NULL,
        out_hash,
        &validation,
        NULL
    );
    if (rc != TSS2_RC_SUCCESS) { return rc; }

    return TSS2_RC_SUCCESS;
}

// 清理资源
void clean(TSS2_TCTI_CONTEXT *tcti_context, TSS2_SYS_CONTEXT *sys_context) {
    if (sys_context != NULL) {
        Tss2_Sys_Finalize(sys_context);
        free(sys_context);
    }
    if (tcti_context != NULL) {
        Tss2_TctiLdr_Finalize(&tcti_context);
    }
}
